/*
 base2 - copyright 2007-2011, Dean Edwards
 http://code.google.com/p/base2/
 http://www.opensource.org/licenses/mit-license.php

 Contributors:
 Doeke Zanstra
 */

var base2 = {
  name:    "base2",
  version: "1.0.2",
  exports:
    "Base,Package,Abstract,Module,Enumerable,Map,Collection,RegGrp," +
      "Undefined,Null,This,True,False,assignID,detect,global",
  namespace: ""
};

new function(_no_shrink_) { ///////////////  BEGIN: CLOSURE  ///////////////

// =========================================================================
// base2/header.js
// =========================================================================

  var Undefined = K(), Null = K(null), True = K(true), False = K(false), This = function(){return this};

  var global = This();
  var base2 = global.base2;

// private
  var _FORMAT = /%([1-9])/g;
  var _LTRIM = /^\s\s*/;
  var _RTRIM = /\s\s*$/;
  var _RESCAPE = /([\/()[\]{}|*+-.,^$?\\])/g;             // safe regular expressions
  var _BASE = /try/.test(detect) ? /\bbase\b/ : /.*/;     // some platforms don't allow decompilation
  var _HIDDEN = ["constructor", "toString", "valueOf"];   // only override these when prototyping
  var _MSIE_NATIVE_FUNCTION = detect("(jscript)") ?
    new RegExp("^" + rescape(isNaN).replace(/isNaN/, "\\w+") + "$") : {test: False};

  var _counter = 1;
  var _slice = Array.prototype.slice;

  _Function_forEach(); // make sure this is initialised

  function assignID(object) {
    // Assign a unique ID to an object.
    if (!object.base2ID) object.base2ID = "b2_" + _counter++;
    return object.base2ID;
  };

// =========================================================================
// base2/Base.js
// =========================================================================

// http://dean.edwards.name/weblog/2006/03/base/

  var _subclass = function(_instance, _static) {
    // Build the prototype.
    base2.__prototyping = this.prototype;
    var _prototype = new this;
    if (_instance) extend(_prototype, _instance);
    delete base2.__prototyping;

    // Create the wrapper for the constructor function.
    var _constructor = _prototype.constructor;
    function _class() {
      // Don't call the constructor function when prototyping.
      if (!base2.__prototyping) {
        if (this.constructor == arguments.callee || this.__constructing) {
          // Instantiation.
          this.__constructing = true;
          _constructor.apply(this, arguments);
          delete this.__constructing;
        } else {
          // Casting.
          return extend(arguments[0], _prototype);
        }
      }
      return this;
    };
    _prototype.constructor = _class;

    // Build the static interface.
    for (var i in Base) _class[i] = this[i];
    _class.ancestor = this;
    _class.base = Undefined;
    //_class.init = Undefined;
    if (_static) extend(_class, _static);
    _class.prototype = _prototype;
    if (_class.init) _class.init();

    // introspection (removed when packed)
    ;;; _class["#implements"] = [];
    ;;; _class["#implemented_by"] = [];

    return _class;
  };

  var Base = _subclass.call(Object, {
    constructor: function() {
      if (arguments.length > 0) {
        this.extend(arguments[0]);
      }
    },

    base: function() {
      // Call this method from any other method to invoke the current method's ancestor (super).
    },

    extend: delegate(extend)
  }, Base = {
    ancestorOf: function(klass) {
      return _ancestorOf(this, klass);
    },

    extend: _subclass,

    forEach: function(object, block, context) {
      _Function_forEach(this, object, block, context);
    },

    implement: function(source) {
      if (typeof source == "function") {
        ;;; if (_ancestorOf(Base, source)) {
          // introspection (removed when packed)
          ;;; this["#implements"].push(source);
          ;;; source["#implemented_by"].push(this);
          ;;; }
        source = source.prototype;
      }
      // Add the interface using the extend() function.
      extend(this.prototype, source);
      return this;
    }
  });

// =========================================================================
// base2/Package.js
// =========================================================================

  var Package = Base.extend({
    constructor: function(_private, _public) {
      this.extend(_public);
      if (this.init) this.init();

      if (this.name && this.name != "base2") {
        if (!this.parent) this.parent = base2;
        this.parent.addName(this.name, this);
        this.namespace = format("var %1=%2;", this.name, String2.slice(this, 1, -1));
      }

      if (_private) {
        // This next line gets round a bug in old Mozilla browsers
        var JSNamespace = base2.JavaScript ? base2.JavaScript.namespace : "";
        // This string should be evaluated immediately after creating a Package object.
        _private.imports = Array2.reduce(csv(this.imports), function(namespace, name) {
          var ns = lookup(name) || lookup("JavaScript." + name);
          ;;; assert(ns, format("Object not found: '%1'.", name), ReferenceError);
          return namespace += ns.namespace;
        }, "var base2=(function(){return this.base2})();" + base2.namespace + JSNamespace) + lang.namespace;

        // This string should be evaluated after you have created all of the objects
        // that are being exported.
        _private.exports = Array2.reduce(csv(this.exports), function(namespace, name) {
          var fullName = this.name + "." + name;
          this.namespace += "var " + name + "=" + fullName + ";";
          return namespace += "if(!" + fullName + ")" + fullName + "=" + name + ";";
        }, "", this) + "this._label_" + this.name + "();";

        var pkg = this;
        var packageName = String2.slice(this, 1, -1);
        _private["_label_" + this.name] = function() {
          Package.forEach (pkg, function(object, name) {
            if (object && object.ancestorOf == Base.ancestorOf) {
              object.toString = K(format("[%1.%2]", packageName, name));
              if (object.prototype.toString == Base.prototype.toString) {
                object.prototype.toString = K(format("[object %1.%2]", packageName, name));
              }
            }
          });
        };
      }

      function lookup(names) {
        names = names.split(".");
        var value = base2, i = 0;
        while (value && names[i] != null) {
          value = value[names[i++]];
        }
        return value;
      };
    },

    exports: "",
    imports: "",
    name: "",
    namespace: "",
    parent: null,

    addName: function(name, value) {
      if (!this[name]) {
        this[name] = value;
        this.exports += "," + name;
        this.namespace += format("var %1=%2.%1;", name, this.name);
      }
    },

    addPackage: function(name) {
      this.addName(name, new Package(null, {name: name, parent: this}));
    },

    toString: function() {
      return format("[%1]", this.parent ? String2.slice(this.parent, 1, -1) + "." + this.name : this.name);
    }
  });

// =========================================================================
// base2/Abstract.js
// =========================================================================

  var Abstract = Base.extend({
    constructor: function() {
      throw new TypeError("Abstract class cannot be instantiated.");
    }
  });

// =========================================================================
// base2/Module.js
// =========================================================================

  var _moduleCount = 0;

  var Module = Abstract.extend(null, {
    namespace: "",

    extend: function(_interface, _static) {
      // Extend a module to create a new module.
      var module = this.base();
      var index = _moduleCount++;
      module.namespace = "";
      module.partial = this.partial;
      module.toString = K("[base2.Module[" + index + "]]");
      Module[index] = module;
      // Inherit class methods.
      module.implement(this);
      // Implement module (instance AND static) methods.
      if (_interface) module.implement(_interface);
      // Implement static properties and methods.
      if (_static) {
        extend(module, _static);
        if (module.init) module.init();
      }
      return module;
    },

    forEach: function(block, context) {
      _Function_forEach (Module, this.prototype, function(method, name) {
        if (typeOf(method) == "function") {
          block.call(context, this[name], name, this);
        }
      }, this);
    },

    implement: function(_interface) {
      var module = this;
      var id = module.toString().slice(1, -1);
      if (typeof _interface == "function") {
        if (!_ancestorOf(_interface, module)) {
          this.base(_interface);
        }
        if (_ancestorOf(Module, _interface)) {
          // Implement static methods.
          for (var name in _interface) {
            if (module[name] === undefined) {
              var property = _interface[name];
              if (typeof property == "function" && property.call && _interface.prototype[name]) {
                property = _staticModuleMethod(_interface, name);
              }
              module[name] = property;
            }
          }
          module.namespace += _interface.namespace.replace(/base2\.Module\[\d+\]/g, id);
        }
      } else {
        // Add static interface.
        extend(module, _interface);
        // Add instance interface.
        _extendModule(module, _interface);
      }
      return module;
    },

    partial: function() {
      var module = Module.extend();
      var id = module.toString().slice(1, -1);
      // partial methods are already bound so remove the binding to speed things up
      module.namespace = this.namespace.replace(/(\w+)=b[^\)]+\)/g, "$1=" + id + ".$1");
      this.forEach(function(method, name) {
        module[name] = partial(bind(method, module));
      });
      return module;
    }
  });

  function _extendModule(module, _interface) {
    var proto = module.prototype;
    var id = module.toString().slice(1, -1);
    for (var name in _interface) {
      var property = _interface[name], namespace = "";
      if (name.charAt(0) == "@") { // object detection
        if (detect(name.slice(1))) _extendModule(module, property);
      } else if (!proto[name]) {
        if (name == name.toUpperCase()) {
          namespace = "var " + name + "=" + id + "." + name + ";";
        } else if (typeof property == "function" && property.call) {
          namespace = "var " + name + "=base2.lang.bind('" + name + "'," + id + ");";
          proto[name] = _moduleMethod(module, name);
          ;;; proto[name]._module = module; // introspection
        }
        if (module.namespace.indexOf(namespace) == -1) {
          module.namespace += namespace;
        }
      }
    }
  };

  function _staticModuleMethod(module, name) {
    return function() {
      return module[name].apply(module, arguments);
    };
  };

  function _moduleMethod(module, name) {
    return function() {
      var args = _slice.call(arguments);
      args.unshift(this);
      return module[name].apply(module, args);
    };
  };

// =========================================================================
// base2/Enumerable.js
// =========================================================================

  var Enumerable = Module.extend({
    every: function(object, test, context) {
      var result = true;
      try {
        forEach (object, function(value, key) {
          result = test.call(context, value, key, object);
          if (!result) throw StopIteration;
        });
      } catch (error) {
        if (error != StopIteration) throw error;
      }
      return !!result; // cast to boolean
    },

    filter: function(object, test, context) {
      var i = 0;
      return this.reduce(object, function(result, value, key) {
        if (test.call(context, value, key, object)) {
          result[i++] = value;
        }
        return result;
      }, []);
    },

    invoke: function(object, method) {
      // Apply a method to each item in the enumerated object.
      var args = _slice.call(arguments, 2);
      return this.map(object, (typeof method == "function") ? function(item) {
        return item == null ? undefined : method.apply(item, args);
      } : function(item) {
        return item == null ? undefined : item[method].apply(item, args);
      });
    },

    map: function(object, block, context) {
      var result = [], i = 0;
      forEach (object, function(value, key) {
        result[i++] = block.call(context, value, key, object);
      });
      return result;
    },

    pluck: function(object, key) {
      return this.map(object, function(item) {
        return item == null ? undefined : item[key];
      });
    },

    reduce: function(object, block, result, context) {
      var initialised = arguments.length > 2;
      forEach (object, function(value, key) {
        if (initialised) {
          result = block.call(context, result, value, key, object);
        } else {
          result = value;
          initialised = true;
        }
      });
      return result;
    },

    some: function(object, test, context) {
      return !this.every(object, not(test), context);
    }
  });

// =========================================================================
// base2/Map.js
// =========================================================================

// http://wiki.ecmascript.org/doku.php?id=proposals:dictionary

  var _HASH = "#";

  var Map = Base.extend({
    constructor: function(values) {
      if (values) this.merge(values);
    },

    clear: function() {
      for (var key in this) if (key.indexOf(_HASH) == 0) {
        delete this[key];
      }
    },

    copy: function() {
      base2.__prototyping = true; // not really prototyping but it stops [[construct]] being called
      var copy = new this.constructor;
      delete base2.__prototyping;
      for (var i in this) if (this[i] !== copy[i]) {
        copy[i] = this[i];
      }
      return copy;
    },

    forEach: function(block, context) {
      for (var key in this) if (key.indexOf(_HASH) == 0) {
        block.call(context, this[key], key.slice(1), this);
      }
    },

    get: function(key) {
      return this[_HASH + key];
    },

    getKeys: function() {
      return this.map(II);
    },

    getValues: function() {
      return this.map(I);
    },

    // Ancient browsers throw an error if we use "in" as an operator.
    has: function(key) {
      /*@cc_on @*/
      /*@if (@_jscript_version < 5.5)
       return $Legacy.has(this, _HASH + key);
       @else @*/
      return _HASH + key in this;
      /*@end @*/
    },

    merge: function(values) {
      var put = flip(this.put);
      forEach (arguments, function(values) {
        forEach (values, put, this);
      }, this);
      return this;
    },

    put: function(key, value) {
      // create the new entry (or overwrite the old entry).
      this[_HASH + key] = value;
    },

    remove: function(key) {
      delete this[_HASH + key];
    },

    size: function() {
      // this is expensive because we are not storing the keys
      var size = 0;
      for (var key in this) if (key.indexOf(_HASH) == 0) size++;
      return size;
    },

    union: function(values) {
      return this.merge.apply(this.copy(), arguments);
    }
  });

  Map.implement(Enumerable);

  Map.prototype.filter = function(test, context) {
    return this.reduce(function(result, value, key) {
      if (!test.call(context, value, key, this)) {
        result.remove(key);
      }
      return result;
    }, this.copy(), this);
  };

// =========================================================================
// base2/Collection.js
// =========================================================================

// A Map that is more array-like (accessible by index).

// Collection classes have a special (optional) property: Item
// The Item property points to a constructor function.
// Members of the collection must be an instance of Item.

// The static create() method is responsible for all construction of collection items.
// Instance methods that add new items (add, put, insertAt, putAt) pass *all* of their arguments
// to the static create() method. If you want to modify the way collection items are
// created then you only need to override this method for custom collections.

  var _KEYS = "~";

  var Collection = Map.extend({
    constructor: function(values) {
      this[_KEYS] = new Array2;
      this.base(values);
    },

    add: function(key, item) {
      // Duplicates not allowed using add().
      // But you can still overwrite entries using put().
      assert(!this.has(key), "Duplicate key '" + key + "'.");
      this.put.apply(this, arguments);
    },

    clear: function() {
      this.base();
      this[_KEYS].length = 0;
    },

    copy: function() {
      var copy = this.base();
      copy[_KEYS] = this[_KEYS].copy();
      return copy;
    },

    forEach: function(block, context) {
      var keys = this[_KEYS];
      var length = keys.length;
      for (var i = 0; i < length; i++) {
        block.call(context, this[_HASH + keys[i]], keys[i], this);
      }
    },

    getAt: function(index) {
      var key = this[_KEYS].item(index);
      return (key === undefined)  ? undefined : this[_HASH + key];
    },

    getKeys: function() {
      return this[_KEYS].copy();
    },

    indexOf: function(key) {
      return this[_KEYS].indexOf(String(key));
    },

    insertAt: function(index, key, item) {
      assert(this[_KEYS].item(index) !== undefined, "Index out of bounds.");
      assert(!this.has(key), "Duplicate key '" + key + "'.");
      this[_KEYS].insertAt(index, String(key));
      this[_HASH + key] = null; // placeholder
      this.put.apply(this, _slice.call(arguments, 1));
    },

    item: function(keyOrIndex) {
      return this[typeof keyOrIndex == "number" ? "getAt" : "get"](keyOrIndex);
    },

    put: function(key, item) {
      if (!this.has(key)) {
        this[_KEYS].push(String(key));
      }
      var klass = this.constructor;
      if (klass.Item && !instanceOf(item, klass.Item)) {
        item = klass.create.apply(klass, arguments);
      }
      this[_HASH + key] = item;
    },

    putAt: function(index, item) {
      arguments[0] = this[_KEYS].item(index);
      assert(arguments[0] !== undefined, "Index out of bounds.");
      this.put.apply(this, arguments);
    },

    remove: function(key) {
      // The remove() method of the Array object can be slow so check if the key exists first.
      if (this.has(key)) {
        this[_KEYS].remove(String(key));
        delete this[_HASH + key];
      }
    },

    removeAt: function(index) {
      var key = this[_KEYS].item(index);
      if (key !== undefined) {
        this[_KEYS].removeAt(index);
        delete this[_HASH + key];
      }
    },

    reverse: function() {
      this[_KEYS].reverse();
      return this;
    },

    size: function() {
      return this[_KEYS].length;
    },

    slice: function(start, end) {
      var sliced = this.copy();
      if (arguments.length > 0) {
        var keys = this[_KEYS], removed = keys;
        sliced[_KEYS] = Array2(_slice.apply(keys, arguments));
        if (sliced[_KEYS].length) {
          removed = removed.slice(0, start);
          if (arguments.length > 1) {
            removed = removed.concat(keys.slice(end));
          }
        }
        for (var i = 0; i < removed.length; i++) {
          delete sliced[_HASH + removed[i]];
        }
      }
      return sliced;
    },

    sort: function(compare) { // optimised (refers to _HASH)
      if (compare) {
        this[_KEYS].sort(bind(function(key1, key2) {
          return compare(this[_HASH + key1], this[_HASH + key2], key1, key2);
        }, this));
      } else this[_KEYS].sort();
      return this;
    },

    toString: function() {
      return "(" + (this[_KEYS] || "") + ")";
    }
  }, {
    Item: null, // If specified, all members of the collection must be instances of Item.

    create: function(key, item) {
      return this.Item ? new this.Item(key, item) : item;
    },

    extend: function(_instance, _static) {
      var klass = this.base(_instance);
      klass.create = this.create;
      if (_static) extend(klass, _static);
      if (!klass.Item) {
        klass.Item = this.Item;
      } else if (typeof klass.Item != "function") {
        klass.Item = (this.Item || Base).extend(klass.Item);
      }
      if (klass.init) klass.init();
      return klass;
    }
  });

// =========================================================================
// base2/RegGrp.js
// =========================================================================

// A collection of regular expressions and their associated replacement values.
// A Base class for creating parsers.

  var _RG_BACK_REF        = /\\(\d+)/g,
    _RG_ESCAPE_CHARS    = /\\./g,
    _RG_ESCAPE_BRACKETS = /\(\?[:=!]|\[[^\]]+\]/g,
    _RG_BRACKETS        = /\(/g,
    _RG_LOOKUP          = /\$(\d+)/,
    _RG_LOOKUP_SIMPLE   = /^\$\d+$/;

  var RegGrp = Collection.extend({
    constructor: function(values, ignoreCase) {
      this.base(values);
      this.ignoreCase = !!ignoreCase;
    },

    ignoreCase: false,

    exec: function(string, override) { // optimised (refers to _HASH/_KEYS)
      string += ""; // type-safe
      var items = this, keys = this[_KEYS];
      if (!keys.length) return string;
      if (override == RegGrp.IGNORE) override = 0;
      return string.replace(new RegExp(this, this.ignoreCase ? "gi" : "g"), function(match) {
        var item, offset = 1, i = 0;
        // Loop through the RegGrp items.
        while ((item = items[_HASH + keys[i++]])) {
          var next = offset + item.length + 1;
          if (arguments[offset]) { // do we have a result?
            var replacement = override == null ? item.replacement : override;
            switch (typeof replacement) {
              case "function":
                return replacement.apply(items, _slice.call(arguments, offset, next));
              case "number":
                return arguments[offset + replacement];
              default:
                return replacement;
            }
          }
          offset = next;
        }
        return match;
      });
    },

    insertAt: function(index, expression, replacement) {
      if (instanceOf(expression, RegExp)) {
        arguments[1] = expression.source;
      }
      return base(this, arguments);
    },

    test: function(string) {
      // The slow way to do it. Hopefully, this isn't called too often. :-)
      return this.exec(string) != string;
    },

    toString: function() {
      var offset = 1;
      return "(" + this.map(function(item) {
        // Fix back references.
        var expression = (item + "").replace(_RG_BACK_REF, function(match, index) {
          return "\\" + (offset + Number(index));
        });
        offset += item.length + 1;
        return expression;
      }).join(")|(") + ")";
    }
  }, {
    IGNORE: "$0",

    init: function() {
      forEach ("add,get,has,put,remove".split(","), function(name) {
        _override(this, name, function(expression) {
          if (instanceOf(expression, RegExp)) {
            arguments[0] = expression.source;
          }
          return base(this, arguments);
        });
      }, this.prototype);
    },

    Item: {
      constructor: function(expression, replacement) {
        if (replacement == null) replacement = RegGrp.IGNORE;
        else if (replacement.replacement != null) replacement = replacement.replacement;
        else if (typeof replacement != "function") replacement = String(replacement);

        // does the pattern use sub-expressions?
        if (typeof replacement == "string" && _RG_LOOKUP.test(replacement)) {
          // a simple lookup? (e.g. "$2")
          if (_RG_LOOKUP_SIMPLE.test(replacement)) {
            // store the index (used for fast retrieval of matched strings)
            replacement = parseInt(replacement.slice(1));
          } else { // a complicated lookup (e.g. "Hello $2 $1")
            // build a function to do the lookup
            // Improved version by Alexei Gorkov:
            var Q = '"';
            replacement = replacement
              .replace(/\\/g, "\\\\")
              .replace(/"/g, "\\x22")
              .replace(/\n/g, "\\n")
              .replace(/\r/g, "\\r")
              .replace(/\$(\d+)/g, Q + "+(arguments[$1]||" + Q+Q + ")+" + Q)
              .replace(/(['"])\1\+(.*)\+\1\1$/, "$1");
            replacement = new Function("return " + Q + replacement + Q);
          }
        }

        this.length = RegGrp.count(expression);
        this.replacement = replacement;
        this.toString = K(expression + "");
      },

      length: 0,
      replacement: ""
    },

    count: function(expression) {
      // Count the number of sub-expressions in a RegExp/RegGrp.Item.
      expression = (expression + "").replace(_RG_ESCAPE_CHARS, "").replace(_RG_ESCAPE_BRACKETS, "");
      return match(expression, _RG_BRACKETS).length;
    }
  });

// =========================================================================
// lang/package.js
// =========================================================================

  var lang = {
    name:      "lang",
    version:   base2.version,
    exports:   "assert,assertArity,assertType,base,bind,copy,extend,forEach,format,instanceOf,match,pcopy,rescape,trim,typeOf",
    namespace: "" // fixed later
  };

// =========================================================================
// lang/assert.js
// =========================================================================

  function assert(condition, message, ErrorClass) {
    if (!condition) {
      throw new (ErrorClass || Error)(message || "Assertion failed.");
    }
  };

  function assertArity(args, arity, message) {
    if (arity == null) arity = args.callee.length;
    if (args.length < arity) {
      throw new SyntaxError(message || "Not enough arguments.");
    }
  };

  function assertType(object, type, message) {
    if (type && (typeof type == "function" ? !instanceOf(object, type) : typeOf(object) != type)) {
      throw new TypeError(message || "Invalid type.");
    }
  };

// =========================================================================
// lang/copy.js
// =========================================================================

  function copy(object) {
    // a quick copy
    var copy = {};
    for (var i in object) {
      copy[i] = object[i];
    }
    return copy;
  };

  function pcopy(object) {
    // Doug Crockford / Richard Cornford
    _dummy.prototype = object;
    return new _dummy;
  };

  function _dummy(){};

// =========================================================================
// lang/extend.js
// =========================================================================

  function base(object, args) {
    return object.base.apply(object, args);
  };

  function extend(object, source) { // or extend(object, key, value)
    if (object && source) {
      if (arguments.length > 2) { // Extending with a key/value pair.
        var key = source;
        source = {};
        source[key] = arguments[2];
      }
      var proto = global[(typeof source == "function" ? "Function" : "Object")].prototype;
      // Add constructor, toString etc
      if (base2.__prototyping) {
        var i = _HIDDEN.length, key;
        while ((key = _HIDDEN[--i])) {
          var value = source[key];
          if (value != proto[key]) {
            if (_BASE.test(value)) {
              _override(object, key, value)
            } else {
              object[key] = value;
            }
          }
        }
      }
      // Copy each of the source object's properties to the target object.
      for (key in source) {
        if (proto[key] === undefined) {
          var value = source[key];
          // Object detection.
          if (key.charAt(0) == "@") {
            if (detect(key.slice(1))) extend(object, value);
          } else {
            // Check for method overriding.
            var ancestor = object[key];
            if (ancestor && typeof value == "function") {
              if (value != ancestor) {
                if (_BASE.test(value)) {
                  _override(object, key, value);
                } else {
                  value.ancestor = ancestor;
                  object[key] = value;
                }
              }
            } else {
              object[key] = value;
            }
          }
        }
      }
    }
    return object;
  };

  function _ancestorOf(ancestor, fn) {
    // Check if a function is in another function's inheritance chain.
    while (fn) {
      if (!fn.ancestor) return false;
      fn = fn.ancestor;
      if (fn == ancestor) return true;
    }
    return false;
  };

  function _override(object, name, method) {
    // Override an existing method.
    var ancestor = object[name];
    var superObject = base2.__prototyping; // late binding for prototypes
    if (superObject && ancestor != superObject[name]) superObject = null;
    function _base() {
      var previous = this.base;
      this.base = superObject ? superObject[name] : ancestor;
      var returnValue = method.apply(this, arguments);
      this.base = previous;
      return returnValue;
    };
    _base.method = method;
    _base.ancestor = ancestor;
    object[name] = _base;
    // introspection (removed when packed)
    ;;; _base.toString = K(method + "");
  };

// =========================================================================
// lang/forEach.js
// =========================================================================

// http://dean.edwards.name/weblog/2006/07/enum/

  if (typeof StopIteration == "undefined") {
    StopIteration = new Error("StopIteration");
  }

  function forEach(object, block, context, fn) {
    if (object == null) return;
    if (!fn) {
      if (typeof object == "function" && object.call) {
        // Functions are a special case.
        fn = Function;
      } else if (typeof object.forEach == "function" && object.forEach != arguments.callee) {
        // The object implements a custom forEach method.
        object.forEach(block, context);
        return;
      } else if (typeof object.length == "number") {
        // The object is array-like.
        _Array_forEach(object, block, context);
        return;
      }
    }
    _Function_forEach(fn || Object, object, block, context);
  };

  forEach.csv = function(string, block, context) {
    forEach (csv(string), block, context);
  };

  forEach.detect = function(object, block, context) {
    forEach (object, function(value, key) {
      if (key.charAt(0) == "@") { // object detection
        if (detect(key.slice(1))) forEach (value, arguments.callee);
      } else block.call(context, value, key, object);
    });
  };

// These are the two core enumeration methods. All other forEach methods
//  eventually call one of these two.

  function _Array_forEach(array, block, context) {
    if (array == null) array = global;
    var length = array.length || 0, i; // preserve length
    if (typeof array == "string") {
      for (i = 0; i < length; i++) {
        block.call(context, array.charAt(i), i, array);
      }
    } else { // Cater for sparse arrays.
      for (i = 0; i < length; i++) {
        /*@cc_on @*/
        /*@if (@_jscript_version < 5.2)
         if ($Legacy.has(array, i))
         @else @*/
        if (i in array)
        /*@end @*/
          block.call(context, array[i], i, array);
      }
    }
  };

  function _Function_forEach(fn, object, block, context) {
    // http://code.google.com/p/base2/issues/detail?id=10

    // Run the test for Safari's buggy enumeration.
    var Temp = function(){this.i=1};
    Temp.prototype = {i:1};
    var count = 0;
    for (var i in new Temp) count++;

    // Overwrite the main function the first time it is called.
    _Function_forEach = (count > 1) ? function(fn, object, block, context) {
      // Safari fix (pre version 3)
      var processed = {};
      for (var key in object) {
        if (!processed[key] && fn.prototype[key] === undefined) {
          processed[key] = true;
          block.call(context, object[key], key, object);
        }
      }
    } : function(fn, object, block, context) {
      // Enumerate an object and compare its keys with fn's prototype.
      for (var key in object) {
        if (fn.prototype[key] === undefined) {
          block.call(context, object[key], key, object);
        }
      }
    };

    _Function_forEach(fn, object, block, context);
  };

// =========================================================================
// lang/instanceOf.js
// =========================================================================

  function instanceOf(object, klass) {
    // Handle exceptions where the target object originates from another frame.
    // This is handy for JSON parsing (amongst other things).

    if (typeof klass != "function") {
      throw new TypeError("Invalid 'instanceOf' operand.");
    }

    if (object == null) return false;

    /*@cc_on
     // COM objects don't have a constructor
     if (typeof object.constructor != "function") {
     return typeOf(object) == typeof klass.prototype.valueOf();
     }
     @*/
    if (object.constructor == klass) return true;
    if (klass.ancestorOf) return klass.ancestorOf(object.constructor);
    /*@if (@_jscript_version < 5.1)
     // do nothing
     @else @*/
    if (object instanceof klass) return true;
    /*@end @*/

    // If the class is a base2 class then it would have passed the test above.
    if (Base.ancestorOf == klass.ancestorOf) return false;

    // base2 objects can only be instances of Object.
    if (Base.ancestorOf == object.constructor.ancestorOf) return klass == Object;

    switch (klass) {
      case Array: // This is the only troublesome one.
        return !!(typeof object == "object" && object.join && object.splice);
      case Function:
        return typeOf(object) == "function";
      case RegExp:
        return typeof object.constructor.$1 == "string";
      case Date:
        return !!object.getTimezoneOffset;
      case String:
      case Number:
      case Boolean:
        return typeOf(object) == typeof klass.prototype.valueOf();
      case Object:
        return true;
    }

    return false;
  };

// =========================================================================
// lang/typeOf.js
// =========================================================================

// http://wiki.ecmascript.org/doku.php?id=proposals:typeof

  function typeOf(object) {
    var type = typeof object;
    switch (type) {
      case "object":
        return object == null
          ? "null"
          : typeof object.constructor == "undefined" // COM object
          ? _MSIE_NATIVE_FUNCTION.test(object)
          ? "function"
          : type
          : typeof object.constructor.prototype.valueOf(); // underlying type
      case "function":
        return typeof object.call == "function" ? type : "object";
      default:
        return type;
    }
  };

// =========================================================================
// JavaScript/package.js
// =========================================================================

  var JavaScript = {
    name:      "JavaScript",
    version:   base2.version,
    exports:   "Array2,Date2,Function2,String2",
    namespace: "", // fixed later

    bind: function(host) {
      var top = global;
      global = host;
      forEach.csv(this.exports, function(name2) {
        var name = name2.slice(0, -1);
        extend(host[name], this[name2]);
        this[name2](host[name].prototype); // cast
      }, this);
      global = top;
      return host;
    }
  };

  function _createObject2(Native, constructor, generics, extensions) {
    // Clone native objects and extend them.

    // Create a Module that will contain all the new methods.
    var INative = Module.extend();
    var id = INative.toString().slice(1, -1);
    // http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6#Array_and_String_generics
    forEach.csv(generics, function(name) {
      INative[name] = unbind(Native.prototype[name]);
      INative.namespace += format("var %1=%2.%1;", name, id);
    });
    forEach (_slice.call(arguments, 3), INative.implement, INative);

    // create a faux constructor that augments the native object
    var Native2 = function() {
      return INative(this.constructor == INative ? constructor.apply(null, arguments) : arguments[0]);
    };
    Native2.prototype = INative.prototype;

    // Remove methods that are already implemented.
    for (var name in INative) {
      if (name != "prototype" && Native[name]) {
        delete INative.prototype[name];
      }
      Native2[name] = INative[name];
    }
    Native2.ancestor = Object;
    delete Native2.extend;

    // remove "lang.bind.."
    Native2.namespace = Native2.namespace.replace(/(var (\w+)=)[^,;]+,([^\)]+)\)/g, "$1$3.$2");

    return Native2;
  };

// =========================================================================
// JavaScript/~/Date.js
// =========================================================================

// Fix Date.get/setYear() (IE5-7)

  if ((new Date).getYear() > 1900) {
    Date.prototype.getYear = function() {
      return this.getFullYear() - 1900;
    };
    Date.prototype.setYear = function(year) {
      return this.setFullYear(year + 1900);
    };
  }

// https://bugs.webkit.org/show_bug.cgi?id=9532

  var _testDate = new Date(Date.UTC(2006, 1, 20));
  _testDate.setUTCDate(15);
  if (_testDate.getUTCHours() != 0) {
    forEach.csv("FullYear,Month,Date,Hours,Minutes,Seconds,Milliseconds", function(type) {
      extend(Date.prototype, "setUTC" + type, function() {
        var value = base(this, arguments);
        if (value >= 57722401000) {
          value -= 3600000;
          this.setTime(value);
        }
        return value;
      });
    });
  }

// =========================================================================
// JavaScript/~/Function.js
// =========================================================================

// Some browsers don't define this.

  Function.prototype.prototype = {};

// =========================================================================
// JavaScript/~/String.js
// =========================================================================

// A KHTML bug.

  if ("".replace(/^/, K("$$")) == "$") {
    extend(String.prototype, "replace", function(expression, replacement) {
      if (typeof replacement == "function") {
        var fn = replacement;
        replacement = function() {
          return String(fn.apply(null, arguments)).split("$").join("$$");
        };
      }
      return this.base(expression, replacement);
    });
  }

// =========================================================================
// JavaScript/Array2.js
// =========================================================================

  var Array2 = _createObject2(
    Array,
    Array,
    "concat,join,pop,push,reverse,shift,slice,sort,splice,unshift", // generics
    Enumerable, {
      combine: function(keys, values) {
        // Combine two arrays to make a hash.
        if (!values) values = keys;
        return Array2.reduce(keys, function(hash, key, index) {
          hash[key] = values[index];
          return hash;
        }, {});
      },

      contains: function(array, item) {
        return Array2.indexOf(array, item) != -1;
      },

      copy: function(array) {
        var copy = _slice.call(array);
        if (!copy.swap) Array2(copy); // cast to Array2
        return copy;
      },

      flatten: function(array) {
        var i = 0;
        return Array2.reduce(array, function(result, item) {
          if (Array2.like(item)) {
            Array2.reduce(item, arguments.callee, result);
          } else {
            result[i++] = item;
          }
          return result;
        }, []);
      },

      forEach: _Array_forEach,

      indexOf: function(array, item, fromIndex) {
        var length = array.length;
        if (fromIndex == null) {
          fromIndex = 0;
        } else if (fromIndex < 0) {
          fromIndex = Math.max(0, length + fromIndex);
        }
        for (var i = fromIndex; i < length; i++) {
          if (array[i] === item) return i;
        }
        return -1;
      },

      insertAt: function(array, index, item) {
        Array2.splice(array, index, 0, item);
        return item;
      },

      item: function(array, index) {
        if (index < 0) index += array.length; // starting from the end
        return array[index];
      },

      lastIndexOf: function(array, item, fromIndex) {
        var length = array.length;
        if (fromIndex == null) {
          fromIndex = length - 1;
        } else if (fromIndex < 0) {
          fromIndex = Math.max(0, length + fromIndex);
        }
        for (var i = fromIndex; i >= 0; i--) {
          if (array[i] === item) return i;
        }
        return -1;
      },

      map: function(array, block, context) {
        var result = [];
        Array2.forEach (array, function(item, index) {
          result[index] = block.call(context, item, index, array);
        });
        return result;
      },

      remove: function(array, item) {
        var index = Array2.indexOf(array, item);
        if (index != -1) Array2.removeAt(array, index);
      },

      removeAt: function(array, index) {
        Array2.splice(array, index, 1);
      },

      swap: function(array, index1, index2) {
        if (index1 < 0) index1 += array.length; // starting from the end
        if (index2 < 0) index2 += array.length;
        var temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
        return array;
      }
    }
  );

  Array2.reduce = Enumerable.reduce; // Mozilla does not implement the thisObj argument

  Array2.like = function(object) {
    // is the object like an array?
    return typeOf(object) == "object" && typeof object.length == "number";
  };

// introspection (removed when packed)
  ;;; Enumerable["#implemented_by"].pop();
  ;;; Enumerable["#implemented_by"].push(Array2);

// =========================================================================
// JavaScript/Date2.js
// =========================================================================

// http://developer.mozilla.org/es4/proposals/date_and_time.html

// big, ugly, regular expression
  var _DATE_PATTERN = /^((-\d+|\d{4,})(-(\d{2})(-(\d{2}))?)?)?T((\d{2})(:(\d{2})(:(\d{2})(\.(\d{1,3})(\d)?\d*)?)?)?)?(([+-])(\d{2})(:(\d{2}))?|Z)?$/;
  var _DATE_PARTS = { // indexes to the sub-expressions of the RegExp above
    FullYear: 2,
    Month: 4,
    Date: 6,
    Hours: 8,
    Minutes: 10,
    Seconds: 12,
    Milliseconds: 14
  };
  var _TIMEZONE_PARTS = { // idem, but without the getter/setter usage on Date object
    Hectomicroseconds: 15, // :-P
    UTC: 16,
    Sign: 17,
    Hours: 18,
    Minutes: 20
  };

  var _TRIM_ZEROES   = /(((00)?:0+)?:0+)?\.0+$/;
  var _TRIM_TIMEZONE = /(T[0-9:.]+)$/;

  var Date2 = _createObject2(
    Date,
    function(yy, mm, dd, h, m, s, ms) {
      switch (arguments.length) {
        case 0: return new Date;
        case 1: return typeof yy == "number" ? new Date(yy) : Date2.parse(yy);
        default: return new Date(yy, mm, arguments.length == 2 ? 1 : dd, h || 0, m || 0, s || 0, ms || 0);
      }
    }, "", {
      toISOString: function(date) {
        var string = "####-##-##T##:##:##.###";
        for (var part in _DATE_PARTS) {
          string = string.replace(/#+/, function(digits) {
            var value = date["getUTC" + part]();
            if (part == "Month") value++; // js month starts at zero
            return ("000" + value).slice(-digits.length); // pad
          });
        }
        // remove trailing zeroes, and remove UTC timezone, when time's absent
        return string.replace(_TRIM_ZEROES, "").replace(_TRIM_TIMEZONE, "$1Z");
      }
    }
  );

  delete Date2.forEach;

  Date2.now = function() {
    return (new Date).valueOf(); // milliseconds since the epoch
  };

  Date2.parse = function(string, defaultDate) {
    if (arguments.length > 1) {
      assertType(defaultDate, "number", "default date should be of type 'number'.")
    }
    // parse ISO date
    var parts = match(string, _DATE_PATTERN);
    if (parts.length) {
      if (parts[_DATE_PARTS.Month]) parts[_DATE_PARTS.Month]--; // js months start at zero
      // round milliseconds on 3 digits
      if (parts[_TIMEZONE_PARTS.Hectomicroseconds] >= 5) parts[_DATE_PARTS.Milliseconds]++;
      var date = new Date(defaultDate || 0);
      var prefix = parts[_TIMEZONE_PARTS.UTC] || parts[_TIMEZONE_PARTS.Hours] ? "UTC" : "";
      for (var part in _DATE_PARTS) {
        var value = parts[_DATE_PARTS[part]];
        if (!value) continue; // empty value
        // set a date part
        date["set" + prefix + part](value);
        // make sure that this setting does not overflow
        if (date["get" + prefix + part]() != parts[_DATE_PARTS[part]]) {
          return NaN;
        }
      }
      // timezone can be set, without time being available
      // without a timezone, local timezone is respected
      if (parts[_TIMEZONE_PARTS.Hours]) {
        var hours = Number(parts[_TIMEZONE_PARTS.Sign] + parts[_TIMEZONE_PARTS.Hours]);
        var minutes = Number(parts[_TIMEZONE_PARTS.Sign] + (parts[_TIMEZONE_PARTS.Minutes] || 0));
        date.setUTCMinutes(date.getUTCMinutes() + (hours * 60) + minutes);
      }
      return date.valueOf();
    } else {
      return Date.parse(string);
    }
  };

// =========================================================================
// JavaScript/String2.js
// =========================================================================

  var String2 = _createObject2(
    String,
    function(string) {
      return new String(arguments.length == 0 ? "" : string);
    },
    "charAt,charCodeAt,concat,indexOf,lastIndexOf,match,replace,search,slice,split,substr,substring,toLowerCase,toUpperCase",
    {
      csv: csv,
      format: format,
      rescape: rescape,
      trim: trim
    }
  );

  delete String2.forEach;

// http://blog.stevenlevithan.com/archives/faster-trim-javascript
  function trim(string) {
    return String(string).replace(_LTRIM, "").replace(_RTRIM, "");
  };

  function csv(string) {
    return string ? (string + "").split(/\s*,\s*/) : [];
  };

  function format(string) {
    // Replace %n with arguments[n].
    // e.g. format("%1 %2%3 %2a %1%3", "she", "se", "lls");
    // ==> "she sells sea shells"
    // Only %1 - %9 supported.
    var args = arguments;
    var pattern = new RegExp("%([1-" + (arguments.length - 1) + "])", "g");
    return (string + "").replace(pattern, function(match, index) {
      return args[index];
    });
  };

  function match(string, expression) {
    // Same as String.match() except that this function will return an empty
    // array if there is no match.
    return (string + "").match(expression) || [];
  };

  function rescape(string) {
    // Make a string safe for creating a RegExp.
    return (string + "").replace(_RESCAPE, "\\$1");
  };

// =========================================================================
// JavaScript/Function2.js
// =========================================================================

  var Function2 = _createObject2(
    Function,
    Function,
    "", {
      I: I,
      II: II,
      K: K,
      bind: bind,
      compose: compose,
      delegate: delegate,
      flip: flip,
      not: not,
      partial: partial,
      unbind: unbind
    }
  );

  function I(i) { // return first argument
    return i;
  };

  function II(i, ii) { // return second argument
    return ii;
  };

  function K(k) {
    return function() {
      return k;
    };
  };

  function bind(fn, context) {
    var lateBound = typeof fn != "function";
    if (arguments.length > 2) {
      var args = _slice.call(arguments, 2);
      return function() {
        return (lateBound ? context[fn] : fn).apply(context, args.concat.apply(args, arguments));
      };
    } else { // faster if there are no additional arguments
      return function() {
        return (lateBound ? context[fn] : fn).apply(context, arguments);
      };
    }
  };

  function compose() {
    var fns = _slice.call(arguments);
    return function() {
      var i = fns.length, result = fns[--i].apply(this, arguments);
      while (i--) result = fns[i].call(this, result);
      return result;
    };
  };

  function delegate(fn, context) {
    return function() {
      var args = _slice.call(arguments);
      args.unshift(this);
      return fn.apply(context, args);
    };
  };

  function flip(fn) {
    return function() {
      return fn.apply(this, Array2.swap(arguments, 0, 1));
    };
  };

  function not(fn) {
    return function() {
      return !fn.apply(this, arguments);
    };
  };

  function partial(fn) {
    var args = _slice.call(arguments, 1);
    // based on Oliver Steele's version
    return function() {
      var specialised = args.concat(), i = 0, j = 0;
      while (i < args.length && j < arguments.length) {
        if (specialised[i] === undefined) specialised[i] = arguments[j++];
        i++;
      }
      while (j < arguments.length) {
        specialised[i++] = arguments[j++];
      }
      if (Array2.contains(specialised, undefined)) {
        specialised.unshift(fn);
        return partial.apply(null, specialised);
      }
      return fn.apply(this, specialised);
    };
  };

  function unbind(fn) {
    return function(context) {
      return fn.apply(context, _slice.call(arguments, 1));
    };
  };

// =========================================================================
// base2/detect.js
// =========================================================================

  function detect() {
    // Two types of detection:
    //  1. Object detection
    //    e.g. detect("(java)");
    //    e.g. detect("!(document.addEventListener)");
    //  2. Platform detection (browser sniffing)
    //    e.g. detect("MSIE");
    //    e.g. detect("MSIE|opera");

    var jscript = NaN/*@cc_on||@_jscript_version@*/; // http://dean.edwards.name/weblog/2007/03/sniff/#comment85164
    var javaEnabled = global.java ? true : false;
    if (global.navigator) { // browser
      var MSIE = /MSIE[\d.]+/g;
      var element = document.createElement("span");
      // Close up the space between name and version number.
      //  e.g. MSIE 6 -> MSIE6
      var userAgent = navigator.userAgent.replace(/([a-z])[\s\/](\d)/gi, "$1$2");
      // Fix opera's (and others) user agent string.
      if (!jscript) userAgent = userAgent.replace(MSIE, "");
      if (MSIE.test(userAgent)) userAgent = userAgent.match(MSIE)[0] + " " + userAgent.replace(MSIE, "");
      base2.userAgent = navigator.platform + " " + userAgent.replace(/like \w+/gi, "");
      javaEnabled &= navigator.javaEnabled();
//} else if (java) { // rhino
//  var System = java.lang.System;
//  base2.userAgent = "Rhino " + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version");
//} else if (jscript) { // Windows Scripting Host
//  base2.userAgent = "WSH";
    }

    var _cache = {};
    detect = function(expression) {
      if (_cache[expression] == null) {
        var returnValue = false, test = expression;
        var not = test.charAt(0) == "!";
        if (not) test = test.slice(1);
        if (test.charAt(0) == "(") {
          try {
            returnValue = new Function("element,jscript,java,global", "return !!" + test)(element, jscript, javaEnabled, global);
          } catch (ex) {
            // the test failed
          }
        } else {
          // Browser sniffing.
          returnValue = new RegExp("(" + test + ")", "i").test(base2.userAgent);
        }
        _cache[expression] = !!(not ^ returnValue);
      }
      return _cache[expression];
    };

    return detect(arguments[0]);
  };

// =========================================================================
// base2/init.js
// =========================================================================

  base2 = global.base2 = new Package(this, base2);
  var exports = this.exports;

  lang = new Package(this, lang);
  exports += this.exports;

  JavaScript = new Package(this, JavaScript);
  eval(exports + this.exports);

  lang.base = base;
  lang.extend = extend;

}; ////////////////////  END: CLOSURE  /////////////////////////////////////
